{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "4c720c3b",
   "metadata": {},
   "source": [
    "# LangChain中的 Prompt 作用\n",
    "简单来说，Prompt 是 “给模型的指令”，它的核心作用是：\n",
    "\n",
    "1. **明确任务目标**：告诉模型 “要做什么”（如 “翻译文本”“总结内容”“生成代码”）。\n",
    "2. **约束输出格式**：指定模型的回答形式（如 “用 JSON 格式输出”“分点列举”）。\n",
    "3. **提供上下文信息**：补充完成任务所需的背景（如对话历史、参考文档片段）。\n",
    "4. **引导模型思维**：通过 “思维链（Chain of Thought）” 等技巧，让模型逐步推理复杂问题。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a7d3dd1f",
   "metadata": {},
   "source": [
    "# LangChain中的 Prompt 类型\n",
    "LangChain 提供了多种 Prompt 类，适配不同场景，最常用的有以下几种：\n",
    "\n",
    "|类型|适用场景|核心特点\n",
    "|:--|:--|:--\n",
    "PromptTemplate|通用文本生成(如问答，总结)|支持变量替换(用 {变量名} 占位)\n",
    "ChatPromptTemplate|对话场景(多轮交互，角色设定)|按\"消息列表\"组织(区分系统指令，用户输入)\n",
    "FewShotPromptTemplate|少样本学习(给模型示例，让其模仿格式)|包含\"示例模板\"和\"示例数据\"\n",
    "SystemMessagePromptTemplate|定义系统角色(如\"你是一个医生\")|作为ChatPromptTemplate的一部分使用\n",
    "\n",
    "以上只是常用的 Prompt，还有很多，请参考官方文档：[LangChain Prompt](https://python.langchain.com/docs/how_to/#prompt-templates)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "29ac5780",
   "metadata": {},
   "source": [
    "## 使用示例\n",
    "以下示例使用的模型为 Deepseek。使用前需要安装一些必要依赖包："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0df92196",
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install langchain openai langchain_community langchain_openai"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1d572133",
   "metadata": {},
   "source": [
    "### 基础类型 PromptTemplate（单轮无角色区分）\n",
    "适用于简单的单轮指令（如翻译、总结、生成代码片段），支持变量动态填充。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "aba15245",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "生成的指令：\n",
      " \n",
      "作为嵌入式工程师，请编写 STM32F103 芯片的 UART串口 初始化代码。\n",
      "要求：\n",
      "1. 使用 C 语言；\n",
      "2. 包含寄存器配置和注释；\n",
      "3. 处理异常情况（如时钟初始化失败）。\n",
      "\n",
      "生成的初始化代码：\n",
      " \n",
      "以下是STM32F103芯片的UART串口初始化代码，包含寄存器配置和异常处理：\n",
      "\n",
      "```c\n",
      "#include \"stm32f10x.h\"\n",
      "#include <stdbool.h>\n",
      "\n",
      "// UART初始化状态枚举\n",
      "typedef enum {\n",
      "    UART_INIT_SUCCESS = 0,\n",
      "    UART_INIT_CLOCK_FAIL,\n",
      "    UART_INIT_GPIO_FAIL,\n",
      "    UART_INIT_CONFIG_ERROR\n",
      "} UART_InitStatus;\n",
      "\n",
      "/**\n",
      " * @brief 初始化UART1\n",
      " * @param baudrate: 波特率\n",
      " * @return UART_InitStatus: 初始化状态\n",
      " */\n",
      "UART_InitStatus UART1_Init(uint32_t baudrate)\n",
      "{\n",
      "    GPIO_InitTypeDef GPIO_InitStructure;\n",
      "    USART_InitTypeDef USART_InitStructure;\n",
      "    \n",
      "    // 使能USART1和GPIOA时钟\n",
      "    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);\n",
      "    \n",
      "    // 检查时钟是否使能成功\n",
      "    if (!(RCC->APB2ENR & RCC_APB2Periph_USART1) || \n",
      "        !(RCC->APB2ENR & RCC_APB2Periph_GPIOA)) {\n",
      "        return UART_INIT_CLOCK_FAIL;\n",
      "    }\n",
      "    \n",
      "    // 配置USART1 Tx (PA9) 为推挽复用输出\n",
      "    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;\n",
      "    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;\n",
      "    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;\n",
      "    GPIO_Init(GPIOA, &GPIO_InitStructure);\n",
      "    \n",
      "    // 配置USART1 Rx (PA10) 为浮空输入\n",
      "    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;\n",
      "    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;\n",
      "    GPIO_Init(GPIOA, &GPIO_InitStructure);\n",
      "    \n",
      "    // 检查GPIO配置是否正确\n",
      "    if ((GPIOA->CRH & GPIO_CRH_CNF9) != (0x0B << 4) ||  // 检查PA9配置\n",
      "        (GPIOA->CRH & GPIO_CRH_CNF10) != (0x04 << 8)) { // 检查PA10配置\n",
      "        return UART_INIT_GPIO_FAIL;\n",
      "    }\n",
      "    \n",
      "    // USART参数配置\n",
      "    USART_InitStructure.USART_BaudRate = baudrate;\n",
      "    USART_InitStructure.USART_WordLength = USART_WordLength_8b;\n",
      "    USART_InitStructure.USART_StopBits = USART_StopBits_1;\n",
      "    USART_InitStructure.USART_Parity = USART_Parity_No;\n",
      "    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;\n",
      "    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;\n",
      "    \n",
      "    // 应用USART1配置\n",
      "    USART_Init(USART1, &USART_InitStructure);\n",
      "    \n",
      "    // 直接寄存器配置检查\n",
      "    // 检查波特率寄存器值是否正确\n",
      "    uint32_t expected_brr = (SystemCoreClock + (baudrate / 2)) / baudrate;\n",
      "    if (USART1->BRR != expected_brr) {\n",
      "        return UART_INIT_CONFIG_ERROR;\n",
      "    }\n",
      "    \n",
      "    // 检查控制寄存器配置\n",
      "    uint32_t expected_cr1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;\n",
      "    if ((USART1->CR1 & expected_cr1) != expected_cr1) {\n",
      "        return UART_INIT_CONFIG_ERROR;\n",
      "    }\n",
      "    \n",
      "    // 使能USART1\n",
      "    USART_Cmd(USART1, ENABLE);\n",
      "    \n",
      "    // 最终状态检查\n",
      "    if (!(USART1->CR1 & USART_CR1_UE)) {\n",
      "        return UART_INIT_CONFIG_ERROR;\n",
      "    }\n",
      "    \n",
      "    return UART_INIT_SUCCESS;\n",
      "}\n",
      "\n",
      "/**\n",
      " * @brief UART1发送一个字符\n",
      " * @param data: 要发送的数据\n",
      " */\n",
      "void UART1_SendChar(uint8_t data)\n",
      "{\n",
      "    // 等待发送缓冲区为空\n",
      "    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);\n",
      "    \n",
      "    // 发送数据\n",
      "    USART_SendData(USART1, data);\n",
      "}\n",
      "\n",
      "/**\n",
      " * @brief UART1接收一个字符（非阻塞）\n",
      " * @param data: 接收数据的指针\n",
      " * @return bool: 是否成功接收到数据\n",
      " */\n",
      "bool UART1_ReceiveChar(uint8_t *data)\n",
      "{\n",
      "    if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET) {\n",
      "        *data = USART_ReceiveData(USART1);\n",
      "        return true;\n",
      "    }\n",
      "    return false;\n",
      "}\n",
      "\n",
      "// 使用示例\n",
      "int main(void)\n",
      "{\n",
      "    // 系统时钟初始化（假设已配置为72MHz）\n",
      "    SystemInit();\n",
      "    \n",
      "    // 初始化UART1，波特率115200\n",
      "    UART_InitStatus status = UART1_Init(115200);\n",
      "    \n",
      "    if (status != UART_INIT_SUCCESS) {\n",
      "        // 处理初始化失败情况\n",
      "        switch (status) {\n",
      "            case UART_INIT_CLOCK_FAIL:\n",
      "                // 时钟初始化失败处理\n",
      "                while(1); // 死循环或错误处理\n",
      "                break;\n",
      "            case UART_INIT_GPIO_FAIL:\n",
      "                // GPIO配置失败处理\n",
      "                while(1);\n",
      "                break;\n",
      "            case UART_INIT_CONFIG_ERROR:\n",
      "                // USART配置失败处理\n",
      "                while(1);\n",
      "                break;\n",
      "            default:\n",
      "                break;\n",
      "        }\n",
      "    }\n",
      "    \n",
      "    // 发送测试数据\n",
      "    UART1_SendChar('H');\n",
      "    UART1_SendChar('i');\n",
      "    UART1_SendChar('\\n');\n",
      "    \n",
      "    uint8_t receivedData;\n",
      "    while (1) {\n",
      "        // 接收数据处理\n",
      "        if (UART1_ReceiveChar(&receivedData)) {\n",
      "            // 回显接收到的数据\n",
      "            UART1_SendChar(receivedData);\n",
      "        }\n",
      "    }\n",
      "}\n",
      "```\n",
      "\n",
      "**代码说明：**\n",
      "\n",
      "1. **时钟配置**：使能USART1和GPIOA的时钟，并检查是否使能成功\n",
      "2. **GPIO配置**：配置PA9为推挽复用输出(Tx)，PA10为浮空输入(Rx)\n",
      "3. **UART参数配置**：设置波特率、数据位、停止位、校验位等参数\n",
      "4. **异常处理**：\n",
      "   - 检查时钟使能状态\n",
      "   - 检查GPIO配置是否正确\n",
      "   - 检查波特率寄存器值\n",
      "   - 检查控制寄存器配置\n",
      "   - 检查最终使能状态\n",
      "\n",
      "5. **收发函数**：提供了基本的字符发送和接收函数\n",
      "\n",
      "**注意事项：**\n",
      "- 需要确保系统时钟已正确配置（通常为72MHz）\n",
      "- 根据实际硬件连接调整GPIO引脚\n",
      "- 可根据需要添加中断配置\n",
      "- 错误处理可根据实际应用需求进行扩展\n",
      "\n",
      "这段代码提供了完整的UART初始化流程和基本的错误检查机制，适合在嵌入式项目中使用。\n"
     ]
    }
   ],
   "source": [
    "from langchain.prompts import PromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "import os\n",
    "\n",
    "api_base = os.getenv(\"OPENAI_API_BASE\")\n",
    "api_key = os.getenv(\"OPENAI_API_KEY\")\n",
    "\n",
    "# 初始化 DeepSeek 模型（需配置 API 密钥）\n",
    "llm = ChatOpenAI(\n",
    "    model_name=\"deepseek-chat\",  # 或 deepseek-coder 用于代码生成\n",
    "    openai_api_base=api_base,\n",
    "    openai_api_key=api_key,\n",
    "    temperature=0.3  # 控制输出随机性（0 表示更严谨）\n",
    ")\n",
    "\n",
    "# 定义 Prompt 模板（包含变量 {chip_model} 和 {function}）\n",
    "template = \"\"\"\n",
    "作为嵌入式工程师，请编写 {chip_model} 芯片的 {function} 初始化代码。\n",
    "要求：\n",
    "1. 使用 C 语言；\n",
    "2. 包含寄存器配置和注释；\n",
    "3. 处理异常情况（如时钟初始化失败）。\n",
    "\"\"\"\n",
    "prompt = PromptTemplate(\n",
    "    input_variables=[\"chip_model\", \"function\"],  # 变量名\n",
    "    template=template\n",
    ")\n",
    "\n",
    "# 填充变量（生成具体指令）\n",
    "filled_prompt = prompt.format(chip_model=\"STM32F103\", function=\"UART串口\")\n",
    "print(\"生成的指令：\\n\", filled_prompt)\n",
    "\n",
    "# 调用 DeepSeek 生成代码\n",
    "response = llm.invoke(filled_prompt)\n",
    "print(\"生成的初始化代码：\\n\", response.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "469e4b33",
   "metadata": {},
   "source": [
    "### ChatPromptTemplate：对话场景（多轮交互 + 角色设定）\n",
    "适用于多轮对话或需要明确角色的场景（如 “你是嵌入式专家”），通过 “系统消息 + 用户消息” 结构组织指令。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "800fc248",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI\n",
    "from langchain.prompts import ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate\n",
    "import os\n",
    "api_base = os.getenv(\"OPENAI_API_BASE\")\n",
    "api_key = os.getenv(\"OPENAI_API_KEY\")\n",
    "\n",
    "# 初始化 DeepSeek 模型（需配置 API 密钥）\n",
    "llm = ChatOpenAI(\n",
    "    model_name=\"deepseek-chat\",  # 或 deepseek-coder 用于代码生成\n",
    "    openai_api_base=api_base,\n",
    "    openai_api_key=api_key,\n",
    "    temperature=0.3  # 控制输出随机性（0 表示更严谨）\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "23185ab5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "调试建议：\n",
      " 好的，这是一个在调试高速SPI时非常典型且棘手的问题。时钟信号上的毛刺是导致数据错位的直接原因，我们必须系统地排查和解决。\n",
      "\n",
      "作为资深工程师，我会从硬件和软件两个层面，由表及里地为您提供排查思路和解决方案。\n",
      "\n",
      "### 核心问题分析：为什么时钟会有毛刺？\n",
      "\n",
      "时钟线上的毛刺（Glitch）本质上是电压的瞬时跳变，通常由以下原因引起：\n",
      "1.  **信号完整性问题**：高速信号在PCB走线上产生反射、振铃。\n",
      "2.  **交叉干扰（Crosstalk）**：时钟线被附近其他高速信号线（如MISO, MOSI）干扰。\n",
      "3.  **电源噪声**：MCU或外部设备的电源不稳，通过输出驱动器耦合到时钟线上。\n",
      "4.  **软件配置问题**：GPIO或SPI外设配置不当，导致多个驱动源短暂冲突。\n",
      "\n",
      "---\n",
      "\n",
      "### 系统性排查与解决步骤\n",
      "\n",
      "请遵循以下步骤，大概率能定位并解决问题。\n",
      "\n",
      "#### 第一步：硬件排查（这是首要任务）\n",
      "\n",
      "1.  **检查PCB布局与走线**：\n",
      "    *   **长度匹配**：SCK、MISO、MOSI、CSN这四条线的长度应尽可能等长，特别是对于高于50MHz的通信。长度不匹配会导致信号边沿错开，在采样窗口内产生不确定性。\n",
      "    *   **阻抗控制与参考平面**：确保信号线有完整的地平面作为参考，且走线阻抗尽量匹配（通常50Ω）。避免跨分割平面，这会严重破坏信号完整性。\n",
      "    *   **远离干扰源**：检查SCK线是否远离其他高频噪声源，如晶振、开关电源电路等。\n",
      "\n",
      "2.  **使用示波器进行深入测量**：\n",
      "    *   **探头校准**：确保您的示波器探头已正确补偿，否则会引入测量误差。\n",
      "    *   **测量点**：一定要将探头地线夹在离MCU引脚最近的地点上（越短越好），测量MCU引脚处的信号，而不是线路末端。\n",
      "    *   **观察毛刺细节**：放大毛刺，看它发生在时钟的上升沿、下降沿还是空闲期间？这能提供线索。\n",
      "    *   **检查电源噪声**：同时测量MCU的VDD和VSS（地）引脚，看看毛刺出现时电源上是否有同步的噪声。如果有，问题根源可能是电源。\n",
      "\n",
      "3.  **端接电阻（Termination）**：\n",
      "    *   这是解决信号完整性问题的**最有效手段**之一。对于高速SPI（特别是线长>10cm），信号在末端反射会造成振铃和过冲，看起来就是毛刺。\n",
      "    *   **方法**：在SCK线的末端（从主设备角度看，即从设备输入端）串联一个小的阻尼电阻（如22Ω - 100Ω），或尝试并联一个端接电阻到地（如50Ω）。串联电阻是最简单且常用的方法。通过调整电阻值，观察示波器上波形的变化，直到振铃消失，边沿变得干净但又不至于过缓。\n",
      "\n",
      "4.  **电源去耦**：\n",
      "    *   确保MCU和从设备电源引脚附近有足够且高质量的去耦电容。**每个VDD引脚**都应有一个100nF的陶瓷电容，并且位置要非常靠近引脚。此外，还应有一个10uF级别的bulk电容在电源入口处。这是稳定运行的基础。\n",
      "\n",
      "#### 第二步：软件配置检查\n",
      "\n",
      "在确保硬件没有明显问题后，再检查软件配置。\n",
      "\n",
      "1.  **GPIO速度配置（非常关键！）**：\n",
      "    *   在STM32H7中，GPIO的输出速度寄存器 (`GPIOx_OSPEEDR`) 必须与您的实际通信速度相匹配。\n",
      "    *   **误区**：不是配置得越高越好。过高的输出驱动速度会导致边沿过于陡峭，加剧信号完整性问题（振铃、过冲、辐射）。\n",
      "    *   **建议**：尝试降低GPIO速度。对于SPI时钟，可以依次尝试 `Low`， `Medium`， `High` 速度，观察毛刺的变化。**通常，选择能可靠传输数据的的最低速度是最佳实践。**\n",
      "\n",
      "    ```c\n",
      "    // 示例：配置SPI SCK引脚为High速度（而非Very High）\n",
      "    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 尝试改为 GPIO_SPEED_FREQ_MEDIUM\n",
      "    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);\n",
      "    ```\n",
      "\n",
      "2.  **SPI时钟极性与相位（CPOL/CPHA）**：\n",
      "    *   确保主设备（STM32）和从设备的CPOL和CPHA模式设置**完全一致**。模式不匹配不会直接产生毛刺，但会导致采样点错误，数据看起来像是“错位”，有时会误判为毛刺问题。再次确认您的从设备数据手册。\n",
      "\n",
      "3.  **时钟预分频器**：\n",
      "    *   暂时大幅降低SPI时钟频率（例如，从50MHz降到10MHz）。如果毛刺消失或显著减弱，那么问题极大概率是硬件上的信号完整性或电源问题。这是一个有效的“二分法”测试。\n",
      "\n",
      "4.  **禁用无关中断**：\n",
      "    *   在发起SPI传输的临界代码段，禁用全局中断或其他高优先级中断（如SysTick），确保SPI数据传输不被打断，避免意外的时间窗口干扰。\n",
      "\n",
      "    ```c\n",
      "    __disable_irq();\n",
      "    // 启动SPI传输（例如HAL_SPI_Transmit）\n",
      "    __enable_irq();\n",
      "    ```\n",
      "\n",
      "### 总结与行动清单\n",
      "\n",
      "1.  **首要行动**：在SCK线上靠近从设备端**串联一个33Ω电阻**，并观察波形变化。这是成本最低、收效最快的实验。\n",
      "2.  **软件调整**：**立即降低GPIO输出速度**和SPI时钟频率，看问题是否改善。\n",
      "3.  **深入测量**：用示波器仔细测量MCU引脚处的SCK和电源波形，寻找关联性。\n",
      "4.  **最终手段**：如果以上均无效，且问题无法规避，则需要考虑修改PCB设计，加强阻抗控制和电源滤波。\n",
      "\n",
      "通过这种硬件与软件结合的系统性排查，您一定能定位并解决这个SPI时钟毛刺的问题。\n"
     ]
    }
   ],
   "source": [
    "# 1.定义系统角色（告诉模型“你的身份和规则”）\n",
    "system_template = \"\"\"\n",
    "你是一名资深的嵌入式工程师，你擅长：\n",
    "- 分析 Cortex-M 系统芯片的外设驱动\n",
    "- 解决硬件与软件交互的BUG\n",
    "- 用简洁的语言解释复杂的寄存器配置\n",
    "回答时需要结合具体的硬件细节，避免泛泛而谈。\n",
    "\"\"\"\n",
    "system_message = SystemMessagePromptTemplate.from_template(system_template)\n",
    "\n",
    "# 2.定义用户消息模板（支持变量{question}）\n",
    "user_template = \"\"\"\n",
    "我在调试{chip}的{peripheral}时，遇到了{issue}，该如何解决？\n",
    "\"\"\"\n",
    "user_message = HumanMessagePromptTemplate.from_template(user_template)\n",
    "\n",
    "# 3.组合成聊天prompt\n",
    "chat_prompt = ChatPromptTemplate.from_messages([\n",
    "    system_message, #系统角色（优先执行）\n",
    "    user_message    #用户当前问题\n",
    "])\n",
    "\n",
    "# 4.填充变量并调用deepseek\n",
    "filled_prompt = chat_prompt.format_prompt(\n",
    "    chip = \"STM32H743\",\n",
    "    peripheral = \"SPI接口\",\n",
    "    issue = \"数据传输时偶尔出现错位，示波器显示时钟信号有毛刺\"\n",
    ").to_messages()\n",
    "\n",
    "response = llm.invoke(filled_prompt)\n",
    "print(\"调试建议：\\n\", response.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a4d675f",
   "metadata": {},
   "source": [
    "### FewShotPromptTemplate：少样本学习（通过示例引导格式）\n",
    "当需要模型遵循特定格式或逻辑时，提供几个示例让其模仿（尤其适合嵌入式领域的结构化任务，如寄存器解析、故障诊断）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "6aea1888",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import FewShotPromptTemplate, PromptTemplate\n",
    "from langchain_openai import ChatOpenAI\n",
    "import os\n",
    "\n",
    "api_base = os.getenv(\"OPENAI_API_BASE\")\n",
    "api_key = os.getenv(\"OPENAI_API_KEY\")\n",
    "\n",
    "# 初始化 DeepSeek 模型（需配置 API 密钥）\n",
    "llm = ChatOpenAI(\n",
    "    model_name=\"deepseek-chat\",  # 或 deepseek-coder 用于代码生成\n",
    "    openai_api_base=api_base,\n",
    "    openai_api_key=api_key,\n",
    "    temperature=0.3  # 控制输出随机性（0 表示更严谨）\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "d0712a06",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "I2C_SR1 寄存器的 bit0 是 SB（起始位）标志位，值为 1 表示已成功生成起始条件（Start condition），主设备可以开始发送从设备地址进行通信。\n"
     ]
    }
   ],
   "source": [
    "# 1.定义示例（告诉模型正确的格式是什么）\n",
    "example = [\n",
    "    {\n",
    "        \"input\": \"寄存器 USART_SR 的 bit5 为 1\",\n",
    "        \"output\": \"USART_SR 寄存器的 bit5 是 TXE（发送数据寄存器空）标志位，值为 1 表示发送缓冲区已空，可写入新数据。\"\n",
    "    },\n",
    "    {\n",
    "        \"input\": \"寄存器 SPI_SR 的 bit1 为 1\",\n",
    "        \"output\": \"SPI_SR 寄存器的 bit1 是 OVR（溢出）标志位，值为 1 表示接收缓冲区未及时读取，发生数据溢出，需清零该位恢复通信。\"\n",
    "    }\n",
    "]\n",
    "\n",
    "# 2.定义示例模板（每个示例的格式）\n",
    "example_template = \"\"\"\n",
    "输入：{input}\n",
    "输出：{output}\n",
    "\"\"\"\n",
    "example_prompt = PromptTemplate(\n",
    "    input_variables = [\"input\", \"output\"],  #指定变量名\n",
    "    template = example_template,\n",
    ")\n",
    "\n",
    "# 3.组合成少样本 Prompt\n",
    "few_shot_prompt = FewShotPromptTemplate(\n",
    "    examples = example,\n",
    "    example_prompt = example_prompt,\n",
    "    prefix = \"请根据以下示例格式，解析嵌入式芯片的寄存器状态（说明位含义和当前值的意义）：\",    #输出前缀\n",
    "    suffix = \"请解析：{register}\",   #输出后缀\n",
    "    input_variables = [\"register\"],\n",
    "    example_separator = \"\\n\",\n",
    ")\n",
    "\n",
    "# 4. 填充变量并调用 DeepSeek\n",
    "filled_prompt = few_shot_prompt.format(register = \"寄存器 I2C_SR1  的 bit0 为 1\")\n",
    "response = llm.invoke(filled_prompt)\n",
    "print(response.content)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
